package com.suse.warmstill.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.suse.warmstill.common.enums.RedisEnum;
import com.suse.warmstill.common.enums.ResponseEnum;
import com.suse.warmstill.common.exception.BusinessException;
import com.suse.warmstill.common.exception.SystemException;
import com.suse.warmstill.common.model.ListPageModel;
import com.suse.warmstill.common.utils.MD5Util;
import com.suse.warmstill.common.utils.RedisCache;

import com.suse.warmstill.common.utils.UserIdThreadLocal;
import com.suse.warmstill.entity.TbToken;
import com.suse.warmstill.entity.TbUser;
import com.suse.warmstill.entity.TbUserinfo;
import com.suse.warmstill.mapper.TbUserMapper;
import com.suse.warmstill.mapper.TbUserinfoMapper;
import com.suse.warmstill.service.*;
import com.suse.warmstill.service.repository.UserRepository;
import com.suse.warmstill.transaction.TransactionService;
import com.suse.warmstill.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class UserServiceImpl implements UserSefvice, BaseService<UserPostVo, TbUser> {

    @Resource
    private RedisCache redisCache;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private TbUserMapper userMapper;

    @Autowired
    private TbUserinfoMapper userinfoMapper;

    @Autowired
    private UserROleService userROleService;

    @Autowired
    private TransactionService transactionService;

    @Resource
    private RedisTemplate<String, Integer> redisTemplate;

    @Resource
    private ChatMsgService chatMsgService;


    /*
     * 获取图片验证码
     * */
    @Override
    public void getLoginImageCode(String uuid, ServletResponse response) throws IOException {
        LineCaptcha shearCaptcha = CaptchaUtil.createLineCaptcha(120, 50, 4, 48);
        String code = shearCaptcha.getCode();
        code = code.toLowerCase();
        String redisKey = RedisEnum.CAPTCHA_KEY + uuid;
        userRepository.saveLoginImageCode(redisKey, code);
        log.info("生成验证码成功,uuid->{},code->{}", uuid, code);
        shearCaptcha.write(response.getOutputStream());

    }

    /*
     * 验证图片验证码
     * */

    public void verifyLoginImageCode(String uuid, String code) {
        String redisKey = RedisEnum.CAPTCHA_KEY + uuid;
        final String lowcode = code.toLowerCase();

        Long capchaExpire = userRepository.getCapchaExpire(redisKey);
        log.info("code->{},redisKey->{},expire->{}", code, redisKey, capchaExpire);
        if (capchaExpire <= 0) {
            throw new BusinessException(ResponseEnum.LOGIN_EXPIRED.getCode(), ResponseEnum.LOGIN_EXPIRED.getMsg());
        }
        String enCode = userRepository.getCaptchaCode(redisKey);
        if (!lowcode.equals(enCode)) {
            throw new BusinessException(ResponseEnum.LOGIN_CAPCHA_CODEERROR.getCode(), ResponseEnum.LOGIN_CAPCHA_CODEERROR.getMsg());
        }


    }

    @Override
    public TbUser toEntity(UserPostVo vo) {
        final TbUser user = new TbUser();
        user.setIsDeleted(0);
        user.setDepId(vo.getDepId());
        user.setPhone(vo.getPhone());
        user.setGmtCreat(LocalDateTime.now());

        user.setPassword(MD5Util.sign(vo.getPassword()));
        user.setUsername(vo.getUsername());
        user.setRelName(vo.getRelName());
        user.setStatus(0);
        return user;
    }

    @Override
    public UserPostVo toVo(TbUser entity) {
        final UserPostVo userPostVo = new UserPostVo();
        return null;
    }

    /**
     * @param loginVo
     * @param request
     * @return 处理登入逻辑
     */
    @Override
    @Transactional
    public Map login(LoginVo loginVo, HttpServletResponse response, HttpServletRequest request) {
        /*
         * 1、校验数据库是否有此账户，进行账号校验
         * 2、踢下线功能
         * 3、判断是否被禁用  status ==1
         * 4、查看是否有信息
         * 5、存储旧token，和accesstoken
         * 、返回token
         * */
        //先校验验证码

        if (!loginVo.getUuid().equals("wxlogin")) {
            verifyLoginImageCode(loginVo.getUuid(), loginVo.getCode());
            //校验登入
        }
        TbUser user = userRepository.getUserByPhone(loginVo.getPhone());
        if (ObjectUtils.isEmpty(user)) {
            //   return CallResult.fail(ResponseEnum.NO_LOGIN_USER.getCode(),ResponseEnum.NO_LOGIN_USER.getMsg());
            throw new SystemException(ResponseEnum.NO_LOGIN_USER.getCode(), ResponseEnum.NO_LOGIN_USER.getMsg());
        }
        final String password = MD5Util.sign(loginVo.getPassword());
        if (!password.equals(user.getPassword())) {
            /*
             * 判断登入次数是否错误，超过 4次，第二次登入在 5小时后，redis存储 5小时
             * */
            final int time = redisTemplate.opsForValue().get(RedisEnum.LIMIT_LOGIN + user.getId());
            if (ObjectUtils.isEmpty(time)) {
                redisTemplate.opsForValue().set(RedisEnum.LIMIT_LOGIN + user.getId(), 0, 1, TimeUnit.HOURS);
            }
            redisTemplate.opsForValue().increment(RedisEnum.LIMIT_LOGIN + user.getId());
            if (time == 4) {
                userMapper.lock(user.getId(), 1);
                throw new SystemException(ResponseEnum.NO_LOGIN_USER.getCode(), "账号密码输入错误 4次，已锁定，请联系管理员进行恢复");
            }
            throw new SystemException(ResponseEnum.NO_LOGIN_USER.getCode(), ResponseEnum.NO_LOGIN_USER.getMsg());
        }
        if (user.getStatus() == 1) {
            throw new SystemException(ResponseEnum.LOGIN_STATUS_ERROR.getCode(), ResponseEnum.LOGIN_STATUS_ERROR.getMsg());
        }
        redisTemplate.delete(RedisEnum.LIMIT_LOGIN + user.getId());
        /*
         * 生成单token
         * */
        String accessToken = tokenService.getaccesstoken(user.getId());
//ACCESS_TOKEN 存储到 redis
        ///*
        //        * 存在 redis 为 12 小时 ，判断是否过期
        //        * */
        //key token+accesstoken   value id
        redisCache.setCacheObject(RedisEnum.LOGIN_TOKEN + accessToken, user.getId(), 12, TimeUnit.HOURS);
        String oldRedisKey = RedisEnum.LOGIN_OLD_USER + user.getId();
        /*
         *踢下线功能
         * 判断redis中是否 存在 token，如果存在 就删除
         * */
        String oldtoken = (String) redisCache.getCacheObject(oldRedisKey);
        //oldtoken 存储的是 accesstoken
        if (StringUtils.isNotEmpty(oldtoken)) {
            //删除旧token
            redisCache.deleteObject(RedisEnum.LOGIN_TOKEN + oldtoken);
        }
        redisCache.setCacheObject(oldRedisKey, accessToken, 12, TimeUnit.HOURS);

        //   String refreshToken = tokenService.getRefleshToken(user.getId());
        //refresh_token 存储到 mysql
        //异步更新 User表
        CompletableFuture.runAsync(() -> {
            user.setGmtLoginTime(LocalDateTime.now());
            userRepository.update(user);
            log.info("用户->{} 登入后台,更新了登入时间", user.getRelName());
        });
        //异步  记录日志           待升级为 rabbitmq 解决
        CompletableFuture.runAsync(() -> {
            final TbToken token = new TbToken();
            token.setUserId(user.getId());
            token.setRefreshToken("pc端无需 refreshtoken");
            token.setAccessToken(accessToken);
            tokenService.insetToken(token);
        });
        /*
         * 把token信息存储到 cookie  12小时
         * */
        final Cookie cookie = new Cookie("accessToken", accessToken);
        cookie.setPath("/");
        cookie.setMaxAge(12 * 3600); //毫秒
        response.addCookie(cookie);
        /*
         * 同时也要返回 token
         * */
        final HashMap<String, String> map = new HashMap<>();
        map.put("accessToken", accessToken);
        map.put("userId", String.valueOf(user.getId()));
        map.put("userName", user.getUsername());
        //  map.put("photoImage",userinfoMapper.selectOne(new LambdaQueryWrapper<TbUserinfo>().eq(TbUserinfo::getUserId,user.getId())).getPhotoPath());
        //处理 websocket登入问题
        return map;
    }

    @Override
    public void inserUser(UserPostVo userVo) {
        final LambdaQueryWrapper<TbUser> tbUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
        tbUserLambdaQueryWrapper.eq(TbUser::getPhone, userVo.getPhone());
        if (ObjectUtils.isNotEmpty(userMapper.selectOne(tbUserLambdaQueryWrapper))) {
            //电话已经注册
            throw new BusinessException(ResponseEnum.USER_REPEAD.getCode(), ResponseEnum.USER_REPEAD.getMsg());
        }


        transactionService.process(() -> {
            //插入User
            final TbUser tbUser = toEntity(userVo);
            userMapper.insert(tbUser);
            final Long id = tbUser.getId();

            final TbUserinfo tbUserinfo = new TbUserinfo();
            tbUserinfo.setUserId(id);
            tbUserinfo.setBirth(userVo.getBirth());
            tbUserinfo.setNickName(userVo.getNickName());
            tbUserinfo.setPhotoPath("https://warmstill-1314066575.cos.ap-chengdu.myqcloud" +
                    ".com/warmstill/2022/10/5/c9d31287-0236-4714-baef-a7bf14a2e301image/jpg");
            tbUserinfo.setSex(userVo.getSex());
            //插入UserInfo
            userinfoMapper.insert(tbUserinfo);

            final Long id1 = tbUserinfo.getId();
            tbUser.setInfoId(id1);
            userMapper.updateById(tbUser);

            //更新用户角色表
            userROleService.insert(id, userVo.getRoleList());
        });
    }


    @Override
    public ListPageModel<UserVo> listUsers(ConditionVO conditionVO) {
        final Page<TbUser> tbUserPage = new Page<TbUser>(conditionVO.getCurrent(), conditionVO.getSize());

        final LambdaQueryWrapper<TbUser> wrapper = new LambdaQueryWrapper<>();
        if (ObjectUtils.isNotEmpty(conditionVO.getKeyword())) {
            wrapper.eq(TbUser::getUsername, conditionVO.getKeyword());
        }
        final Page<TbUser> page = userMapper.selectPage(tbUserPage, wrapper);

//pagemodel的组装
        final ListPageModel<UserVo> pageModel = new ListPageModel<>();
        pageModel.setPageSize((int) page.getSize());
        pageModel.setPageCount((int) page.getPages());
        pageModel.setCurrentPage((int) page.getCurrent());
        pageModel.setDataSize((int) page.getTotal());


        final ArrayList<UserVo> userVos = new ArrayList<>();

        /*
         * 无角色的用户
         * */
        final ArrayList<String> users = new ArrayList<>();

        page.getRecords().stream().forEach((user) -> {
            final UserVo userVo = new UserVo();
            userVo.setId(user.getId());
            userVo.setGmtCreat(user.getGmtCreat());
            userVo.setUsername(user.getUsername());
            userVo.setInfoId(user.getInfoId());
            userVo.setStatus(user.getStatus());
            userVo.setRelName(user.getRelName());
            userVo.setPhone(user.getPhone());


            final Long userId = user.getId();
            //开始组装 menuList
            //先拿到对应的角色

            final List<Long> roleIds = userROleService.getRoleIdByUserId(userId);
            if (CollectionUtil.isEmpty(roleIds)) {
                users.add(user.getUsername());
            } else {
                userVo.setRoleId(roleIds);

   /*        //查询menu的id list

            final List<Long> menuList = roleMenuService.listMenuByRoleId(roleId);
            userVo.setMenuList(menuList);*/
                userVos.add(userVo);

            }
        });

        if (CollectionUtil.isNotEmpty(users)) {
            throw new BusinessException(ResponseEnum.USER_NOROLE.getCode(), ResponseEnum.USER_NOROLE.getMsg() + "用户分别是: " + users);
        }
        pageModel.setList(userVos);


        return pageModel;
    }


    @Override
    public void LogicDeleteUser(Long userId) {
        userMapper.deleteById(userId);
    }

    @Override
    public void foribddenUser(Long userId, int status) {
        userMapper.updateUserStatus(userId, status);
    }


    @Override
    public void logoutUser(Long userId, HttpServletRequest request, HttpServletResponse response) {

        /*
         * 用户 退出
         * 1、删除双 token
         *
         *   redis的 accesstoken   Oldtoken+token
         * 2、删除 redis的 旧token Oldtoken+id
         * 3、删除 cookie   accessToken
         *
         * */

        // String token = null;
        String oldToken = RedisEnum.LOGIN_OLD_USER + UserIdThreadLocal.get();
        String token = redisCache.getCacheObject(oldToken);


        final Cookie cookie = new Cookie("accessToken", null);
        cookie.setPath("/");
        cookie.setMaxAge(1); //毫秒
        response.addCookie(cookie);

        String tokenKey = RedisEnum.LOGIN_TOKEN + token;

        redisCache.deleteObject(tokenKey);
        redisCache.deleteObject(oldToken);
        // userMapper.deleteRefreshTokenById(userId);

    }


}
